package com.childenglish.controller;

import com.childenglish.entity.SysUser;
import com.childenglish.service.FileStorageService;
import com.childenglish.service.FileChunkService;
import com.childenglish.mapper.FileInfoMapper;
import com.childenglish.utils.R;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/file")
public class FileController {

    private static final Logger logger = LoggerFactory.getLogger(FileController.class);

    @Autowired
    private FileStorageService fileStorageService;

    @Autowired
    private FileChunkService fileChunkService;
    
    @Autowired
    private FileInfoMapper fileInfoMapper;

    /**
     * 普通文件上传（小文件）
     */
    @PostMapping("/upload")
    public R uploadFile(
            @RequestParam("file") MultipartFile file,
            @RequestParam(value = "fileType", required = false) String fileType) {
        try {
            logger.info("收到文件上传请求: fileName={}, size={} bytes, contentType={}, fileType={}", 
                    file != null ? file.getOriginalFilename() : "null",
                    file != null ? file.getSize() : 0,
                    file != null ? file.getContentType() : "null",
                    fileType);
            
            if (file == null || file.isEmpty()) {
                logger.warn("文件上传失败: 文件为空");
                return R.error("文件不能为空");
            }

            String fileName = fileStorageService.storeFile(file, fileType);

            Map<String, Object> result = new HashMap<>();
            result.put("fileName", fileName);
            result.put("originalName", file.getOriginalFilename());
            result.put("size", file.getSize());
            result.put("contentType", file.getContentType());
            result.put("uploadTime", System.currentTimeMillis());

            logger.info("文件上传成功: originalName={}, savedFileName={}, size={} bytes, fileType={}", 
                    file.getOriginalFilename(), fileName, file.getSize(), fileType);
            
            return R.ok("文件上传成功").data(result);

        } catch (Exception e) {
            logger.error("文件上传失败", e);
            return R.error("文件上传失败: " + (e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName()));
        }
    }

    /**
     * 大文件分片上传 - 检查分片状态
     */
    @GetMapping("/chunk/status")
    public R checkChunkStatus(
            @RequestParam String fileMd5,
            @RequestParam String fileName,
            @RequestParam Long fileSize,
            @RequestParam Integer chunkSize,
            @RequestParam Integer totalChunks) {
        try {
            Map<String, Object> status = fileChunkService.checkChunkStatus(
                    fileMd5, fileName, fileSize, chunkSize, totalChunks);
            return R.ok("分片状态查询成功").data(status);
        } catch (Exception e) {
            return R.error("分片状态查询失败: " + e.getMessage());
        }
    }

    /**
     * 大文件分片上传 - 上传分片
     */
    @PostMapping("/chunk/upload")
    public R uploadChunk(
            @RequestParam String fileMd5,
            @RequestParam String fileName,
            @RequestParam Integer chunkNumber,
            @RequestParam Integer totalChunks,
            @RequestParam Long chunkSize,
            @RequestParam Long fileSize,
            @RequestParam("chunk") MultipartFile chunk) {
        try {
            logger.info("收到分片上传请求: fileMd5={}, fileName={}, chunk={}/{}, chunkSize={} bytes",
                    fileMd5, fileName, chunkNumber, totalChunks, chunk.getSize());
            Map<String, Object> result = fileChunkService.uploadChunk(
                    fileMd5, fileName, chunkNumber, totalChunks, chunkSize, fileSize, chunk);
            logger.info("分片上传完成: fileMd5={}, chunk={}", fileMd5, chunkNumber);
            return R.ok("分片上传成功").data(result);
        } catch (Exception e) {
            logger.error("分片上传失败: fileMd5={}, chunk={}", fileMd5, chunkNumber, e);
            return R.error("分片上传失败: " + e.getMessage());
        }
    }

    /**
     * 大文件分片上传 - 合并分片
     */
    @PostMapping("/chunk/merge")
    public R mergeChunks(
            @RequestParam String fileMd5,
            @RequestParam String fileName,
            @RequestParam Integer totalChunks,
            @RequestParam(required = false) String uploadUser, // 保留参数以兼容旧版本，但优先使用Session中的用户
            @RequestParam(required = false) String fileType) {
        try {
            // 优先从Shiro Session获取当前登录用户
            String currentUploadUser = null;
            try {
                Subject subject = SecurityUtils.getSubject();
                if (subject.isAuthenticated()) {
                    SysUser currentUser = (SysUser) subject.getPrincipal();
                    if (currentUser != null) {
                        currentUploadUser = currentUser.getUsername();
                        logger.info("从Session获取当前登录用户: {}", currentUploadUser);
                    }
                }
            } catch (Exception e) {
                logger.warn("从Session获取用户失败，使用前端传递的参数: {}", e.getMessage());
            }
            
            // 如果Session中没有用户，使用前端传递的参数（兼容旧版本）
            if (currentUploadUser == null || currentUploadUser.isEmpty()) {
                currentUploadUser = uploadUser;
                logger.info("使用前端传递的上传用户: {}", currentUploadUser);
            }
            
            logger.info("收到文件合并请求: fileMd5={}, fileName={}, totalChunks={}, uploadUser={}, fileType={}", 
                fileMd5, fileName, totalChunks, currentUploadUser, fileType);
            Map<String, Object> result = fileChunkService.mergeChunks(fileMd5, fileName, totalChunks, currentUploadUser, fileType);
            logger.info("文件合并完成: fileMd5={}, result={}", fileMd5, result);
            return R.ok("文件合并成功").data(result);
        } catch (Exception e) {
            logger.error("文件合并失败: fileMd5={}, fileName={}", fileMd5, fileName, e);
            return R.error("文件合并失败: " + e.getMessage());
        }
    }

    /**
     * 文件下载（支持断点续传）
     */
    @GetMapping("/download/{fileName:.+}")
    public ResponseEntity<Resource> downloadFile(
            @PathVariable String fileName,
            @RequestHeader(value = "Range", required = false) String rangeHeader) {
        try {
            // 支持断点续传
            if (rangeHeader != null && rangeHeader.startsWith("bytes=")) {
                return fileChunkService.downloadWithRange(fileName, rangeHeader);
            }

            // 普通下载
            logger.info("开始下载文件: {}", fileName);
            byte[] fileData = fileStorageService.loadFile(fileName);
            
            if (fileData == null || fileData.length == 0) {
                logger.error("文件数据为空: {}", fileName);
                return ResponseEntity.status(404).build();
            }
            
            logger.info("文件下载准备完成: {}, 大小: {} bytes", fileName, fileData.length);
            
            ByteArrayResource resource = new ByteArrayResource(fileData);
            
            // 获取原始文件名用于下载
            String originalName = fileStorageService.getOriginalFileName(fileName);
            String downloadFileName = originalName != null && !originalName.equals(fileName) ? originalName : fileName;

            HttpHeaders headers = new HttpHeaders();
            headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + 
                    java.net.URLEncoder.encode(downloadFileName, "UTF-8") + "\"");
            headers.add(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, must-revalidate");
            headers.add(HttpHeaders.PRAGMA, "no-cache");
            headers.add(HttpHeaders.EXPIRES, "0");
            headers.add(HttpHeaders.ACCEPT_RANGES, "bytes");

            return ResponseEntity.ok()
                    .headers(headers)
                    .contentLength(fileData.length)
                    .contentType(MediaType.APPLICATION_OCTET_STREAM)
                    .body(resource);

        } catch (Exception e) {
            return ResponseEntity.notFound().build();
        }
    }

    /**
     * 获取文件下载信息（支持断点续传）
     */
    @GetMapping("/download/info/{fileName:.+}")
    public R getDownloadInfo(@PathVariable String fileName) {
        try {
            Map<String, Object> downloadInfo = fileChunkService.getDownloadInfo(fileName);
            return R.ok("获取下载信息成功").data(downloadInfo);
        } catch (Exception e) {
            return R.error("获取下载信息失败: " + e.getMessage());
        }
    }

    /**
     * 获取文件信息
     */
    @GetMapping("/info/{fileName:.+}")
    public R getFileInfo(@PathVariable String fileName) {
        try {
            Map<String, Object> fileInfo = fileStorageService.getFileInfo(fileName);
            return R.ok("获取文件信息成功").data(fileInfo);
        } catch (Exception e) {
            return R.error("获取文件信息失败: " + e.getMessage());
        }
    }

    /**
     * 文件预览
     */
    @GetMapping("/preview/{fileName:.+}")
    public ResponseEntity<Resource> previewFile(@PathVariable String fileName) {
        try {
            logger.info("文件预览请求: {}", fileName);
            
            byte[] fileData = fileStorageService.loadFile(fileName);
            
            if (fileData == null || fileData.length == 0) {
                logger.error("预览文件数据为空: {}", fileName);
                return ResponseEntity.notFound().build();
            }
            
            ByteArrayResource resource = new ByteArrayResource(fileData);
            
            // 获取文件MIME类型
            String contentType = "application/octet-stream";
            try {
                Path filePath = Paths.get(fileStorageService.getUploadDir()).resolve(fileName);
                if (Files.exists(filePath)) {
                    String detectedType = Files.probeContentType(filePath);
                    if (detectedType != null) {
                        contentType = detectedType;
                    }
                }
            } catch (Exception e) {
                logger.warn("无法检测文件类型: {}", fileName, e);
            }
            
            // 获取原始文件名用于Content-Disposition
            String originalName = fileStorageService.getOriginalFileName(fileName);
            String displayName = originalName != null && !originalName.equals(fileName) ? originalName : fileName;

            HttpHeaders headers = new HttpHeaders();
            // 使用 inline 而不是 attachment，这样浏览器会尝试预览而不是下载
            headers.add(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + 
                    java.net.URLEncoder.encode(displayName, "UTF-8") + "\"");
            headers.add(HttpHeaders.CONTENT_TYPE, contentType);
            headers.add(HttpHeaders.CACHE_CONTROL, "public, max-age=3600");

            logger.info("文件预览准备完成: {}, 大小: {} bytes, contentType: {}", 
                    fileName, fileData.length, contentType);

            return ResponseEntity.ok()
                    .headers(headers)
                    .contentLength(fileData.length)
                    .body(resource);

        } catch (Exception e) {
            logger.error("文件预览失败: {}", fileName, e);
            return ResponseEntity.notFound().build();
        }
    }

    /**
     * 删除文件
     */
    @DeleteMapping("/delete/{fileName:.+}")
    public R deleteFile(@PathVariable String fileName) {
        try {
            boolean deleted = fileStorageService.deleteFile(fileName);
            if (deleted) {
                return R.ok("文件删除成功");
            } else {
                return R.error("文件删除失败，文件可能不存在");
            }
        } catch (Exception e) {
            return R.error("文件删除失败: " + e.getMessage());
        }
    }

    /**
     * 清理临时分片文件
     */
    @DeleteMapping("/chunk/cleanup")
    public R cleanupChunks(@RequestParam String fileMd5) {
        try {
            boolean cleaned = fileChunkService.cleanupChunks(fileMd5);
            if (cleaned) {
                return R.ok("临时文件清理成功");
            } else {
                return R.error("临时文件清理失败");
            }
        } catch (Exception e) {
            return R.error("临时文件清理失败: " + e.getMessage());
        }
    }

    /**
     * 通过MD5删除所有相关记录（用于清理残留数据）
     */
    @DeleteMapping("/deleteByMd5/{fileMd5}")
    public R deleteByMd5(@PathVariable String fileMd5) {
        try {
            // 删除所有相同MD5的记录
            int deletedCount = fileInfoMapper.deleteByFileMd5(fileMd5);
            if (deletedCount > 0) {
                // 清理分片文件
                fileChunkService.cleanupChunks(fileMd5);
                return R.ok("删除成功，共删除 " + deletedCount + " 条记录");
            } else {
                return R.error("未找到相关记录");
            }
        } catch (Exception e) {
            logger.error("通过MD5删除失败: fileMd5={}", fileMd5, e);
            return R.error("删除失败: " + e.getMessage());
        }
    }

    /**
     * 获取文件列表
     */
    @GetMapping("/list")
    public R getFileList(@RequestParam(required = false) String type) {
        try {
            logger.info("获取文件列表请求，类型筛选: {}", type);
            List<Map<String, Object>> fileList = fileStorageService.getFileList(type);
            logger.info("文件列表查询完成，找到 {} 个文件", fileList.size());
            if (fileList.size() > 0) {
                logger.debug("文件列表详情: {}", fileList);
            }
            return R.ok("获取文件列表成功").data(fileList);
        } catch (Exception e) {
            logger.error("获取文件列表失败", e);
            return R.error("获取文件列表失败: " + e.getMessage());
        }
    }

    /**
     * 健康检查
     */
    @GetMapping("/health")
    public R healthCheck() {
        try {
            Map<String, Object> healthInfo = new HashMap<>();
            healthInfo.put("status", "UP");
            healthInfo.put("service", "FileStorageService");
            healthInfo.put("timestamp", System.currentTimeMillis());
            healthInfo.put("uploadDir", "uploads");
            healthInfo.put("maxFileSize", "10MB");
            healthInfo.put("chunkSupport", true);
            healthInfo.put("resumeSupport", true);

            return R.ok("文件服务运行正常").data(healthInfo);
        } catch (Exception e) {
            return R.error("文件服务异常: " + e.getMessage());
        }
    }
}